{
 "cells": [
  {
   "source": [
    "# C04. 当索引行不通时——字典\n",
    "\n",
    "需要将一系列值组合成数据结构，并且通过编号来访问各个值时，使用「列表」。\n",
    "\n",
    "需要将一系列值组合成数据结构，并且通过名称来访问各个值时，使用「映射」。\n",
    "\n",
    "-   字典是 Python 中唯一的内置映射类型，其中的值不按顺序排列，而是存储在键下\n",
    "    -   键可能是数、字符串、元组。"
   ],
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   }
  },
  {
   "source": [
    "## 4.1 字典概述\n",
    "\n",
    "字典：通过特定的单词(键)，获得其定义(值)。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "3158\n"
     ]
    }
   ],
   "source": [
    "# 使用列表实现的字典\n",
    "names=['Alice','Beth','Cecil','Dee-Dee','Earl']\n",
    "numbers=['2341','9102','3158','0142','5551']\n",
    "print(numbers[names.index('Cecil')])"
   ]
  },
  {
   "source": [
    "## 4.2 字典操作\n",
    "\n",
    "字典：由键(key)及其对应的值(value)组成，这种「键-值对」称为项(item)\n",
    "\n",
    "注：在字典(以及其他映射类型)中，键必须是独一无二的，而字典中的值则可以重复。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "3158\n"
     ]
    }
   ],
   "source": [
    "# 创建和使用字典\n",
    "phonebook={'Alice':'2341','Beth':'9102','Cecil':'3158'}\n",
    "print(phonebook['Cecil'])"
   ]
  },
  {
   "source": [
    "### 4.2.1 dict()\n"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{'name': 'Gumby', 'age': 42}\nGumby\n"
     ]
    }
   ],
   "source": [
    "items=[('name','Gumby'),('age',42)]\n",
    "d=dict(items)\n",
    "print(d)\n",
    "print(d['name'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{'name': 'Gumby', 'age': 42}\nGumby\n"
     ]
    }
   ],
   "source": [
    "d=dict(name='Gumby',age=42)\n",
    "print(d)\n",
    "print(d['name'])"
   ]
  },
  {
   "source": [
    "### 4.2.2 基本操作\n",
    "\n",
    "-   `len(d)`：返回字典包含的项的个数\n",
    "-   `d[k]`：返回与键 k 相关联的值\n",
    "-   `d[k]=v`：将值 v 关联到键 k\n",
    "-   `del d[k]`：删除键为 k 的项\n",
    "-   `k in d`：检查字典 d 中是否包含键为 k 的项\n",
    "\n",
    "字典与列表的区别：\n",
    "\n",
    "-   键的类型：可以是整数、浮点数(实数)、字符串、元组等等任何不可变的类型\n",
    "-   自动添加：对字典中的键赋值时，如果存在就覆盖；如果不存在就自动新建\n",
    "-   成员资格：表达式 `k in dict` 查找的是键而不是值；表达式 `v in list` 查找的是值而不是索引\n",
    "\n",
    "注：检查字典是否包含某个键的速度 快于 检查列表是否包含某个值的速度"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{42: 'Foobar'}\n"
     ]
    }
   ],
   "source": [
    "x=[]\n",
    "# x[42]='Foobar' # IndexError\n",
    "x={}\n",
    "x[42]='Foobar'\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Beths phone number is 9102.\n"
     ]
    }
   ],
   "source": [
    "# Code 4-1 字典示例\n",
    "# 一个简单的数据库：人名作为键；字典作为值(phone 和 addr 作为键)\n",
    "people={\n",
    "    'Alice':{\n",
    "        'phone': '2341',\n",
    "        'addr':'Foo drive 23'\n",
    "    },\n",
    "    'Beth':{\n",
    "        'phone':'9102',\n",
    "        'addr':'Bar street 42'\n",
    "    },\n",
    "    'Cecil':{\n",
    "        'phone':'3158',\n",
    "        'addr':'Baz avenue 90'\n",
    "    }\n",
    "}\n",
    "\n",
    "# 电话号码和地址的描述性标签，供打印输出时使用\n",
    "labels={\n",
    "    'phone':'phone number',\n",
    "    'addr':'address'\n",
    "}\n",
    "\n",
    "name=input('Name: ')\n",
    "\n",
    "# 要查找 phone number 还是 address\n",
    "request=input('Phone number (p) or address (a)?')\n",
    "\n",
    "# 使用正确的键：\n",
    "if request == 'p' : key = 'phone'\n",
    "if request == 'a' : key = 'addr'\n",
    "\n",
    "# 仅当名字是字典包含的键时才打印信息：\n",
    "if name in people: print(\"{}s {} is {}.\".format(name,labels[key],people[name][key]))"
   ]
  },
  {
   "source": [
    "### 4.2.3 将字符串格式设置功能用于字典\n",
    "\n"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Cecil's phone number is 3258.\n"
     ]
    }
   ],
   "source": [
    "phonebook={'Beth':'9102','Alice':'2341','Cecil':'3258'}\n",
    "print(\"Cecil's phone number is {Cecil}.\".format_map(phonebook))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "<html>\n<head><title>My Home Page</title></head>\n<body>\n<h1>My Home Page</h1>\n<p>Welcome to my home page!</p>\n</body>\n"
     ]
    }
   ],
   "source": [
    "template='''<html>\n",
    "<head><title>{title}</title></head>\n",
    "<body>\n",
    "<h1>{title}</h1>\n",
    "<p>{text}</p>\n",
    "</body>'''\n",
    "data={'title':\"My Home Page\",'text':\"Welcome to my home page!\"}\n",
    "print(template.format_map(data))"
   ]
  },
  {
   "source": [
    "### 4.2.4 字典方法\n"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d= {'name': 'Gumby', 'age': 42}\nd= {}\nreturned_value= None\n"
     ]
    }
   ],
   "source": [
    "# 1. clear()：删除所有的字典项，没有返回\n",
    "d={}\n",
    "d['name']='Gumby'\n",
    "d['age']=42\n",
    "print(\"d=\",d)\n",
    "returned_value=d.clear()\n",
    "print(\"d=\",d)\n",
    "print(\"returned_value=\",returned_value)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "y= {'key': 'value'}\ny= {'key': 'value'}\n"
     ]
    }
   ],
   "source": [
    "# 这种方法只是对 x 进行了重新初始化\n",
    "x={}\n",
    "y=x\n",
    "x['key']='value'\n",
    "print(\"y=\",y)\n",
    "x={}\n",
    "print(\"y=\",y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "y= {'key': 'value'}\ny= {}\n"
     ]
    }
   ],
   "source": [
    "# 这种方式才能删除原字典中的所有项\n",
    "x={}\n",
    "y=x\n",
    "x['key']='value'\n",
    "print(\"y=\",y)\n",
    "x.clear()\n",
    "print(\"y=\",y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "x= {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}\nx= {'username': 'admin', 'machines': ['foo', 'baz']}\n"
     ]
    }
   ],
   "source": [
    "# 2. copy()：返回一个新字典，包含的键-值对与原字典相同(这个方法执行的是浅复制，即值本身依然是原件，而不是复本)\n",
    "x={'username':'admin','machines':['foo','bar','baz']}\n",
    "y=x.copy()\n",
    "print(\"x=\",x)\n",
    "y['username']='mlh'\n",
    "y['machines'].remove('bar')\n",
    "print(\"x=\",x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "c= {'names': ['Alfred', 'Bertrand', 'Clive']}\ndc= {'names': ['Alfred', 'Bertrand']}\n"
     ]
    }
   ],
   "source": [
    "# 使用深复制\n",
    "from copy import deepcopy\n",
    "d={}\n",
    "d['names']=['Alfred','Bertrand']\n",
    "c=d.copy()\n",
    "dc=deepcopy(d)\n",
    "d['names'].append('Clive')\n",
    "print(\"c=\",c)\n",
    "print(\"dc=\",dc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{'name': None, 'age': None}\n{'name': None, 'age': None}\n{'name': '(unknown)', 'age': '(unknown)'}\n"
     ]
    }
   ],
   "source": [
    "# 3. fromkeys()：创建一个新字典，其中包含指定的键，且每个键对应的值都是 None\n",
    "print({}.fromkeys(['name','age']))\n",
    "print(dict.fromkeys(['name','age']))\n",
    "print(dict.fromkeys(['name','age'],'(unknown)'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d.get('name')= None\nd.get('name','N/A')= N/A\nd.get('name')= None\nd.get('name')= Eric\n"
     ]
    }
   ],
   "source": [
    "# 4. get()：为访问字典项提供更加保险的方式\n",
    "d={}\n",
    "# print(d['name']) # KeyError\n",
    "print(\"d.get('name')=\",d.get('name'))\n",
    "print(\"d.get('name','N/A')=\",d.get('name','N/A'))\n",
    "print(\"d.get('name')=\",d.get('name'))\n",
    "d['name']='Eric'\n",
    "print(\"d.get('name')=\",d.get('name'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Gumbys batting average is not available.\n"
     ]
    }
   ],
   "source": [
    "# Code 4-2 字典示例(修改版本)\n",
    "# 一个简单的数据库：人名作为键；字典作为值(phone 和 addr 作为键)\n",
    "people={\n",
    "    'Alice':{\n",
    "        'phone': '2341',\n",
    "        'addr':'Foo drive 23'\n",
    "    },\n",
    "    'Beth':{\n",
    "        'phone':'9102',\n",
    "        'addr':'Bar street 42'\n",
    "    },\n",
    "    'Cecil':{\n",
    "        'phone':'3158',\n",
    "        'addr':'Baz avenue 90'\n",
    "    }\n",
    "}\n",
    "\n",
    "# 电话号码和地址的描述性标签，供打印输出时使用\n",
    "labels={\n",
    "    'phone':'phone number',\n",
    "    'addr':'address'\n",
    "}\n",
    "\n",
    "name=input('Name: ')\n",
    "\n",
    "# 要查找 phone number 还是 address\n",
    "request=input('Phone number (p) or address (a)?')\n",
    "\n",
    "# 使用正确的键：\n",
    "key=request\n",
    "if request == 'p' : key = 'phone'\n",
    "if request == 'a' : key = 'addr'\n",
    "\n",
    "# 使用 get() 提供默认值\n",
    "person=people.get(name,{})\n",
    "label=labels.get(key,key)\n",
    "result=person.get(key,'not available')\n",
    "\n",
    "print(\"{}s {} is {}.\".format(name,label,result))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d.items()= dict_items([('title', 'Python Web Site'), ('url', 'http://www.python.org'), ('spam', 0)])\nlist(d.items())= [('title', 'Python Web Site'), ('url', 'http://www.python.org'), ('spam', 0)]\nlen(it)= 3\n('spam',0) in it= True\nd.items()= dict_items([('title', 'Python Web Site'), ('url', 'http://www.python.org'), ('spam', 1)])\nlist(d.items())= [('title', 'Python Web Site'), ('url', 'http://www.python.org'), ('spam', 0)]\n"
     ]
    }
   ],
   "source": [
    "# 5. items()：返回一个包含所有字典项的列表，其中每个元素都为 (key,value) 的形式\n",
    "# 注：字典项中在列表中的排列顺序不确定\n",
    "d={'title':'Python Web Site','url':'http://www.python.org','spam':0}\n",
    "print(\"d.items()=\",d.items())\n",
    "lt=list(d.items())\n",
    "print(\"list(d.items())=\",lt)\n",
    "# 返回值属于一种名为字典视图的特殊类型。\n",
    "# 字典视图可以用于迭代，还可以确定其长度及对其进行成员资格检查。\n",
    "it=d.items()\n",
    "print(\"len(it)=\",len(it))\n",
    "print(\"('spam',0) in it=\",('spam',0) in it)\n",
    "# 字典视图始终是底层字典的反映，对底层字典的修改可以立即影响视图，因为视图并没有对字典进行复制\n",
    "d['spam']=1\n",
    "print(\"d.items()=\",d.items())\n",
    "# 如果需要复制可以直接生成列表\n",
    "print(\"list(d.items())=\",lt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d.keys()= dict_keys(['title', 'url', 'spam'])\n"
     ]
    }
   ],
   "source": [
    "# 6. keys()：返回一个字典视图，其中包含指定字典中的键\n",
    "print(\"d.keys()=\",d.keys())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d= {'x': 1, 'y': 2}\nd= {'y': 2}\n"
     ]
    }
   ],
   "source": [
    "# 7. pop()：用于获取与指定键相关联的值，并且将该「键-值对」从字典中删除。\n",
    "d={'x':1,'y':2}\n",
    "print(\"d=\",d)\n",
    "d.pop('x')\n",
    "print(\"d=\",d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d= {'x': 1, 'y': 2, 'z': 3, 'a': 11, 'b': 12, 'c': 13}\nd= {'x': 1, 'y': 2, 'z': 3, 'a': 11, 'b': 12}\nd= {'x': 1, 'y': 2, 'z': 3, 'a': 11}\n"
     ]
    }
   ],
   "source": [
    "# 8. popitem()：类似于 list.pop()，区别在于 list.pop() 弹出列表中的最后一个元素；dict.popitem() 随机地弹出一个字典项，可以用于顺序处理并且删除所有字典项操作，因为无需获取键列表操作\n",
    "d={'x':1,'y':2,'z':3}\n",
    "d['a']=11\n",
    "d['b']=12\n",
    "d['c']=13\n",
    "print(\"d=\",d)\n",
    "d.popitem()\n",
    "print(\"d=\",d)\n",
    "d.popitem()\n",
    "print(\"d=\",d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d= {}\nd= {'name': 'N/A'}\nd= {'name': 'Gumby'}\nGumby\nNone\nd= {'name': 'Gumby', 'addr': None}\n"
     ]
    }
   ],
   "source": [
    "# 9. setdefault()：获取与指定键相关联的值，与 get() 的区别是：如果字典中不存在这个键，则自动添加指定的「键-值对」\n",
    "d={}\n",
    "print(\"d=\",d)\n",
    "d.setdefault('name','N/A')\n",
    "print(\"d=\",d)\n",
    "d['name']='Gumby'\n",
    "print(\"d=\",d)\n",
    "print(d.setdefault('name'))\n",
    "print(d.setdefault('addr'))\n",
    "print(\"d=\",d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "d= {'title': 'Python Web Site', 'url': 'http://www.python.org', 'changed': 'Mar 14 22:09:15 MET 2016'}\nd= {'title': 'Python Language Website', 'url': 'http://www.python.org', 'changed': 'Mar 14 22:09:15 MET 2016'}\nd= {'title': 'Python的主页', 'url': 'http://www.python.org', 'changed': 'Mar 14 22:09:15 MET 2016'}\n"
     ]
    }
   ],
   "source": [
    "# 10. update()：使用一个字典中的项来更新另一个字典\n",
    "d={\n",
    "    'title':'Python Web Site',\n",
    "    'url':'http://www.python.org',\n",
    "    'changed':'Mar 14 22:09:15 MET 2016'\n",
    "}\n",
    "x={'title':'Python Language Website'}\n",
    "print(\"d=\",d)\n",
    "d.update(x)\n",
    "print(\"d=\",d)\n",
    "y={'title':'Python的主页'}\n",
    "dict.update(d,y)\n",
    "print(\"d=\",d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "dict_values([1, 2, 3, 1])\n"
     ]
    }
   ],
   "source": [
    "# 11. values()：返回一个由字典中的值组成的字典视图。与 keys() 的区别：返回的是值视图，不是键视图；返回的视图中可能包含重复的值。\n",
    "d={}\n",
    "d[1]=1\n",
    "d[2]=2\n",
    "d[3]=3\n",
    "d[4]=1\n",
    "print(d.values())"
   ]
  },
  {
   "source": [
    "## 4.3 小结\n",
    "\n",
    "-   映射：使用任何不可变的对象(常用字符串和元组)来标识其元素\n",
    "    -   Python 只有一种内置的映射类型(字典)\n",
    "-   将字符串格式设置功能应用于字典：format_map()\n",
    "-   字典方法：与列表和字符串的方法大多相同\n",
    "\n",
    "### 4.3.1 新函数\n",
    "\n",
    "-   dict(seq)：从「键-值对」、映射或者关键字参数中创建字典"
   ],
   "cell_type": "markdown",
   "metadata": {}
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "3.6.12-final"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}